package com.common.security.config;


import com.common.core.constant.RoleNameConstant;
import com.common.security.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;

/**
 * 比较AccessDecisionManager类中的角色信息
 */
@Component
public class CustomAccessDecisionManager implements AccessDecisionManager {

    @Autowired
    private HttpServletRequest request;

    @Resource
    private TokenService tokenService;

    /**
     * 重写decide方法，在该方法中判断当前登录的用户是否具有当前请求URL所需的角色信息。如果没有，则抛出AccessDeniedException，否则不执行任何操作。
     *
     * @param auth   当前登录用户的信息
     * @param object FilterInvocationObject，可以获取当前请求对象
     * @param ca     FilterInvocationSecurityMetadataSource中getAttributes方法的返回值是当前请求URL所需的角色
     */
    @Override
    public void decide(Authentication auth, Object object, Collection<ConfigAttribute> ca) {
        String token = request.getHeader("token");

        Collection<? extends GrantedAuthority> auths = auth.getAuthorities();
        for (ConfigAttribute configAttribute : ca) {
            if (RoleNameConstant.PUBLIC.equals(configAttribute.getAttribute())) {
                return;
            }
            /*
             * 如果所需的角色是role_LOGIN，则意味着用户登录后可以访问当前请求的URL。
			 * 如果auth是UsernamePasswordAuthenticationToken的实例，则表示当前用户已登录，方法在此处结束，否则进入正常判断过程。
             */
            if (RoleNameConstant.LOGIN.equals(configAttribute.getAttribute()) && tokenService.verifyToken(token)) {
                tokenService.setAuthentication(token);
                return;
            }

            for (GrantedAuthority authority : auths) {
				// 如果当前用户具有当前请求所需的角色，则该方法结束
                if (configAttribute.getAttribute().equals(authority.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("no permission");
    }
 
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }
 
    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}